using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Reflection;
using NetMonitor.Things;
using System.Threading;
using System.Data.OleDb;
using System.Xml;
using Sanford.Multimedia.Midi;
using DirectMidi;

namespace NetMonitor
{
    public enum DragEnum { None, TempThing, StartHandle, EndHandle, Move };

    public partial class NetworkMap : UserControl
    {
        public Sound sound = new Sound();
        private OutputDevice outDevice;
        private int outDeviceID = 0;
        

        public Form1 mainForm;
        DragEnum dragging = DragEnum.EndHandle;
        Color thingColor = Color.Red;
        int x, y, clickX, clickY;
        bool error = false;
        List<Thing> things = new List<Thing>();
        int mGrid = 20;
        Thing tempThing = null;
        int CurrentViewThingId = 0;
        public bool LogToDb = false;
        const string XML_CONFIG_FILE = "config.xml";
       
        public NetworkMap()
        {
            
            ThreadPool.SetMaxThreads(4, 4);
            InitializeComponent();
            this.DoubleBuffered = true;
    
      
            Type t = typeof(Things.Thing);
            Type[] typeList = Assembly.GetExecutingAssembly().GetTypes();
                
            foreach (Type type in typeList)
            {
                string[] className = type.ToString().Split('.');
                if (className[1].StartsWith("Things") && className[2] != "Thing")
                    toolStripComboBoxThings.Items.Add(className[2]);
            }
            
      
        }

        void initSound()
        {
            if (OutputDevice.DeviceCount == 0)
            {
                MessageBox.Show("No MIDI output devices available.", "Error!",
                    MessageBoxButtons.OK, MessageBoxIcon.Stop);
            }
            else
            {
                try
                {
                    outDevice = new OutputDevice(outDeviceID);
                    outDevice.Send(new ChannelMessage(ChannelCommand.ProgramChange, 0, 10));
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Error!",
                        MessageBoxButtons.OK, MessageBoxIcon.Stop);
                }
            }
        }

        Thing CreateThing(string _className)
        {
            Type[] typeList = Assembly.GetExecutingAssembly().GetTypes();
            foreach (Type type in typeList)
            {
                string[] className = type.ToString().Split('.');
                if (className[1].StartsWith("Things") && className[2] == _className)
                {
                    // Get constructor info. 
                    Type[] empty = new Type[0];
                    ConstructorInfo ci = type.GetConstructor(empty);
                    object reflectObj = ci.Invoke(null);
                    return (Thing)reflectObj;
                }
            }
            return null;
        }


        public void load()
        {
            //initSound();
            //outDevice.Send(new ChannelMessage(ChannelCommand.NoteOn, 0, 44, 127));
            loadAll();
            return;        
        }


        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            dragging = DragEnum.None;
            if (toolStripButtonSelect.CheckState == CheckState.Checked)
            {
                foreach (Thing t in things)
                    if (t.validateClick(e.X, e.Y) == 1)
                    {
                        if (!t.selected)
                        {
                            unselectThings();
                            t.selected = true;
                            
                            tempThing = t;
                        } 
                        clickX = e.X - (int)t.x;
                        clickY =e.Y  -(int  )t.y;
                        dragging = DragEnum.Move;    
                    }
                    else if (t.selected && t.validateClick(e.X, e.Y) == 2) // start handle
                    {
                        dragging = DragEnum.StartHandle;

                    }
                    else if (t.selected && t.validateClick(e.X, e.Y) == 3) // end handle
                    {
                        dragging = DragEnum.EndHandle;
                    }
                this.Refresh();
                return;
            }


            if (toolStripComboBoxThings.SelectedText != "")
            {
                x = e.X;
                y = e.Y;
                dragging = DragEnum.TempThing;
                tempThing = CreateThing(toolStripComboBoxThings.SelectedText);
                tempThing.x = x;
                tempThing.y = y;
                tempThing.w = 0;
                tempThing.h = 0;
                tempThing.col = Color.Blue;
                tempThing.setNetworkMap(this);
                tempThing.setSound(outDevice);
                tempThing.parentThingId = CurrentViewThingId;
                // get max id
                int maxId = -1;
                foreach (Thing t in things)
                    if (t.id > maxId) maxId = t.id;
                tempThing.id = maxId + 1;
            }
        }


        private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                foreach (Thing t in things)
                    if (t.validateClick(e.X, e.Y) == 1)
                    {
                                              
                        ThingProperties pp = new ThingProperties(t);
                        if (pp.ShowDialog() == DialogResult.OK)
                        {
                            //t.save();
                        }                         
                    }                
            }

            dragging = DragEnum.None;
            if (toolStripButtonSelect.CheckState == CheckState.Checked)
            {
                
                foreach (Thing t in things)
                    if (t.validateClick(e.X, e.Y) == 1)
                    {
                        unselectThings();
                        t.selected = true;                       
                    }
                this.Refresh();
                return;
            }

            if (tempThing != null)
            {
                Thing t2 = tempThing; 
                things.Add(t2);
                tempThing = null;
            }
            this.Refresh();
        }


        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            int x = e.X - e.X % mGrid;
            int y = e.Y - e.Y % mGrid;

            if (dragging == DragEnum.TempThing && tempThing != null)
            {
                tempThing.w = x - tempThing.x;
                tempThing.h = y - tempThing.y;  
            }
            else if (dragging == DragEnum.StartHandle && tempThing != null)
            {
                tempThing.w += tempThing.x - x;
                tempThing.h += tempThing.y - y;
                tempThing.x = x;
                tempThing.y = y;
            }
            else if (dragging == DragEnum.EndHandle && tempThing != null)
            {
                tempThing.w = x - tempThing.x;
                tempThing.h = y - tempThing.y;
            }
            else if (dragging == DragEnum.Move && tempThing != null)
            {
                tempThing.x = x - clickX;
                tempThing.y = y - clickY;
            }

            if (tempThing != null)
            {
                tempThing.w -= tempThing.w % mGrid;
                tempThing.h -= tempThing.h % mGrid;
                tempThing.x -= tempThing.x % mGrid;
                tempThing.y -= tempThing.y % mGrid;
            }

            this.Refresh();
        }


        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;

            foreach (Thing t in things)
            {
                if (t.selected == false && t.parentThingId == CurrentViewThingId)
                    t.draw(g);
            }
            
            if (tempThing != null && tempThing.parentThingId == CurrentViewThingId)
                tempThing.draw(g);
            
        }


       

        void uncheckButtons()
        {
            unselectThings();
            toolStripButtonSelect.CheckState = CheckState.Unchecked;
            toolStripButtonNew.CheckState = CheckState.Unchecked;
        }


        private void toolStripButtonSelect_Click(object sender, EventArgs e)
        {
            uncheckButtons();
            toolStripButtonSelect.CheckState = CheckState.Checked;
        }


        public void unselectThings()
        {
            foreach (Thing t in things)
                t.selected = false;
        }

        private void toolStripButtonDelete_Click(object sender, EventArgs e)
        {
            foreach (Thing t in things)
            {
                if (t.selected)
                {
                    things.Remove(t);
                    break;
                }
            }
            tempThing = null;
            Refresh();
        }



        private void toolStripButtonSave_Click(object sender, EventArgs e)
        {      
            saveAll();
        }

        private void timerRedraw_Tick(object sender, EventArgs e)
        {
            Refresh();
            bool err = false;
            foreach (Thing t in things)
                if (t.col == Color.Red)
                {
                    err = true;
                    break;
                }
            setError(err);
            foreach (Thing t in things)
            {
                int now = Environment.TickCount & Int32.MaxValue;
                int dif = now - t.lastRunTime;
                if (t != null && dif > t.interval && !t.queued)
                {
                    if (ThreadPool.QueueUserWorkItem(t.pingerThreadFunc,null))
                    {
                        t.queued = true;
                    }
                }
            }                                   
        }

        public void setError(bool _error)
        {
            if (error != _error)
                if (_error)
                {//going bad
                    Console.Beep(1370, 200);
                    Console.Beep(1004, 200);
                    Console.Beep(891, 200);
                    this.BackColor = Color.DarkRed;
                }
                else
                {// going good
                    Console.Beep(1000, 200);
                    Console.Beep(1250, 200);
                    Console.Beep(1500, 200);
                    this.BackColor = Color.Black;
                }
            error = _error;
        }

        private void toolStripButtonNew_Click(object sender, EventArgs e)
        {
            uncheckButtons();
            toolStripButtonNew.CheckState = CheckState.Checked;
        }

        private void NetworkMap_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            foreach (Thing t in things)
                if (t.validateClick(e.X, e.Y) == 1)
                {
                    unselectThings();
                    CurrentViewThingId = t.id;
                    Refresh();
                }
        }

        private void toolStripButtonHome_Click(object sender, EventArgs e)
        {
            CurrentViewThingId = 0;
            Refresh();
        }

        private void toolStripButtonLogging_Click(object sender, EventArgs e)
        {
            toolStripButtonLogging.Checked = !toolStripButtonLogging.Checked;
            LogToDb = toolStripButtonLogging.Checked;
        }


        void saveAll()
        {
            XmlDocument xmlDoc = new XmlDocument();
            XmlDeclaration dec = xmlDoc.CreateXmlDeclaration("1.0", null, null);
            xmlDoc.AppendChild(dec);// Create the root element
            XmlElement root = xmlDoc.CreateElement("root");
            xmlDoc.AppendChild(root);
            foreach (Thing t in things)
            {
                Type type = t.GetType();            
                FieldInfo[] fia = type.GetFields();

                XmlElement thingElem = xmlDoc.CreateElement("Thing");
                thingElem.SetAttribute("type", type.Name);
                
                foreach (FieldInfo fi in fia)
                    if (fi.IsPublic &&
                    fi.Name != "col" &&
                    fi.Name != "selected" &&
                    fi.Name != "lastRunTime" &&
                    fi.Name != "queued")
                    {
                        XmlElement paramElem = xmlDoc.CreateElement(fi.Name);                        
                        paramElem.SetAttribute("type", fi.FieldType.Name);
                        XmlText text = xmlDoc.CreateTextNode(fi.GetValue(t).ToString());
                        paramElem.AppendChild(text);
                        thingElem.AppendChild(paramElem);
                    }
                root.AppendChild(thingElem);
            }
            xmlDoc.Save(XML_CONFIG_FILE);
        }

        

        void loadAll()
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(XML_CONFIG_FILE);

            XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Thing");
            things.RemoveRange(0, things.Count);
            foreach (XmlNode node in nodeList)
            {
                Thing newThing = CreateThing(node.Attributes["type"].Value);
                if (newThing == null) continue;
                newThing.setNetworkMap(this);
                newThing.setSound(outDevice);
                Type type = newThing.GetType();
                FieldInfo[] fia = type.GetFields();
                
                foreach (XmlNode paramNode in node.ChildNodes)
                {
                    foreach (FieldInfo fi in fia) 
                        if (fi.IsPublic && fi.Name == paramNode.Name)
                        {
                            switch (fi.FieldType.Name)
                            {
                                case "Int32": 
                                    int i=0;
                                    int.TryParse(paramNode.InnerText, out i);
                                    fi.SetValue(newThing, i);
                                    break;
                                case "String":
                                    fi.SetValue(newThing, paramNode.InnerText);
                                    break;
                                case "Boolean":
                                    if (paramNode.InnerText.ToLower() == "true")
                                        fi.SetValue(newThing, true);
                                    else
                                        fi.SetValue(newThing, false);
                                    break;
                            }                            
                        }
                }
                things.Add(newThing);
            }           
        }
 
    }
}
